#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#===============================================================================
# Copyright 2011 zod.yslin
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# 
# Author: zod.yslin
# Email: 
# File Name: string_format.py
# Description: 
#   A new system for built-in string formatting operations replaces the 
#   % string formatting operator. 
#   (However, the % operator is still supported; it will be deprecated in 
#   Python 3.1 and removed from the language at some later time.) 
#   
#   PEP 3101 -- Advanced String Formatting
# Edit History: 
#   2011-10-28    File created.
#===============================================================================

# String Methods
a = "Hello"
b = "String"
d = "python"
print("The story of {0}, {1}, and {c}".format(a, b, c=d))

print(format(10.0, "7.3g"))

# Format Strings
print("My name is {0}".format('Fred')) # "My name is Fred"
# Braces can be escaped by doubling
print("My name is {0} :-{{}}".format('Fred')) # "My name is Fred :-{}"

"""
The element within the braces is called a 'field'.  Fields consist
of a 'field name', which can either be simple or compound, and an
optional 'format specifier'.
"""

# Simple and Compound Field Names
"""
Simple field names are either names or numbers.  If numbers, they
must be valid base-10 integers; if names, they must be valid
Python identifiers.  A number is used to identify a positional
argument, while a name is used to identify a keyword argument.

A compound field name is a combination of multiple simple field
names in an expression.

This example shows the use of the 'getattr' or 'dot' operator
in a field expression.  The dot operator allows an attribute of
an input value to be specified as the field value.
"""
# An example of the 'dot' syntax:
print("My name is {0.name}".format(open('out.txt', 'w')))

"""
Only two operators are supported: the '.' (getattr) operator, 
and the '[]' (getitem) operator.  
The reason for allowing these operators is that they don't
normally have side effects in non-pathological code.
"""
# An example of the 'getitem' syntax:
print("My name is {0[name]}".format(dict(name='Fred')))

"""
In the above example, the string 'name' really is the literal string 'name', 
not a variable named 'name'.  The rules for parsing an item key are very simple.
If it starts with a digit, then it is treated as a number, otherwise it is used as a string.

Because keys are not quote-delimited, it is not possible to
specify arbitrary dictionary keys (e.g., the strings "10" or
":-]") from within a format string.
"""

# Format Specifiers
"""
Each field can also specify an optional set of 'format
specifiers' which can be used to adjust the format of that field.
Format specifiers follow the field name, with a colon (':')
character separating the two:
"""
print("My name is {0:8}".format('Fred'))

"""
Format specifiers can themselves contain replacement fields.
For example, a field whose field width is itself a parameter
could be specified via:
"""
print("{0:{1}}".format(3, 8))

# Standard Format Specifiers
"""
If an object does not define its own format specifiers, a standard
set of format specifiers is used.  These are similar in concept to
the format specifiers used by the existing '%' operator, however
there are also a number of differences.

The general form of a standard format specifier is:

    [[fill]align][sign][#][0][minimumwidth][.precision][type]

The brackets ([]) indicate an optional element.
"""
print("Today is: {0:b}".format(7))  # b is binary

"""
Objects are able to define their own format specifiers to
replace the standard ones.  An example is the 'datetime' class,
whose format specifiers might look something like the
arguments to the strftime() function:
"""
from datetime import datetime
print("Today is: {0:%a %b %d %H:%M:%S %Y}".format(datetime.now()))

# Explicit Conversion Flag
"""
The explicit conversion flag is used to transform the format field value
before it is formatted.  This can be used to override the type-specific
formatting behavior, and format the value as if it were a more
generic type.  Currently, two explicit conversion flags are
recognized:

    !r - convert the value to a string using repr().
    !s - convert the value to a string using str().
"""
print("{0!r:20}".format("Hello"))

# Controlling Formatting on a Per-Type Basis
"""
Each Python type can control formatting of its instances by defining
a __format__ method.  The __format__ method is responsible for
interpreting the format specifier, formatting the value, and
returning the resulting string.

The new, global built-in function 'format' simply calls this special
method, similar to how len() and str() simply call their respective
special methods:
    def format(value, format_spec):
        return value.__format__(format_spec)

Several built-in types, including 'str', 'int', 'float', and 'object'
define __format__ methods.  This means that if you derive from any of
those types, your class will know how to format itself.

The object.__format__ method is the simplest: It simply converts the
object to a string, and then calls format again:
    class object:
        def __format__(self, format_spec):
            return format(str(self), format_spec)
"""

# User-Defined Formatting
"""
An application can subclass the Formatter class to create its own
customized formatting behavior.
"""
# Formatter Methods
import string
#help(string.Formatter)

# Customizing Formatters
class NamespaceFormatter(string.Formatter):
    def __init__(self, namespace={}):
        string.Formatter.__init__(self)
        self.namespace = namespace

    def get_value(self, key, args, kwds):
        if isinstance(key, str):
            try:
                # Check explicitly passed arguments first
                return kwds[key]
            except KeyError:
                return self.namespace[key]
        else:
            string.Formatter.get_value(key, args, kwds)

fmt = NamespaceFormatter(globals())
greeting = "hello"
#print(globals())
print(fmt.format("{greeting}, world!"))


